package com.lonely.order.service.impl;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.convert.Convert;
import cn.hutool.core.lang.Assert;
import cn.hutool.core.map.MapUtil;
import cn.hutool.core.util.NumberUtil;
import cn.hutool.core.util.StrUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.lonely.common.core.page.Condition;
import com.lonely.common.enums.CommonStatus;
import com.lonely.common.enums.DataScope;
import com.lonely.common.exception.BusinessException;
import com.lonely.common.threadlocals.DataPermissionThreadLocal;
import com.lonely.common.utils.SecurityUtils;
import com.lonely.product.domain.entity.SysCuttingBed;
import com.lonely.product.domain.entity.SysCuttingBedSize;
import com.lonely.product.service.ICuttingBedService;
import com.lonely.product.service.ICuttingBedSizeService;
import com.lonely.goods.domain.dto.goods.GoodsQuotationSheetPartSecondProcessDTO;
import com.lonely.goods.domain.entity.SysGoodsSecondProcess;
import com.lonely.goods.enums.GoodsEnum;
import com.lonely.goods.service.IGoodsSecondProcessService;
import com.lonely.order.domain.dto.secondprocess.*;
import com.lonely.order.domain.entity.SysOrder;
import com.lonely.order.domain.entity.SysOrderPart;
import com.lonely.order.domain.entity.SysOrderSecondProcess;
import com.lonely.order.domain.vo.secondprocess.QueryOrderSecondProcessPageQueryVO;
import com.lonely.order.enums.OrderSecondProcessEnum;
import com.lonely.order.mapper.SysOrderSecondProcessMapper;
import com.lonely.order.service.IOrderSecondProcessLogService;
import com.lonely.order.service.IOrderSecondProcessService;
import com.lonely.order.service.IOrderService;
import com.lonely.product.service.IOrderSewingService;
import com.lonely.system.utils.AuthUtil;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.math.BigDecimal;
import java.util.*;
import java.util.stream.Collectors;

/**
 * @author : 黄志标
 * @date: 2025/7/2 10:45
 * @description: 订单二次工艺服务实现类
 */
@Service
public class OrderSecondProcessServiceImpl extends ServiceImpl<SysOrderSecondProcessMapper, SysOrderSecondProcess> implements IOrderSecondProcessService {

    @Autowired
    private IGoodsSecondProcessService goodsSecondProcessService;

    @Autowired
    private IOrderService orderService;

    @Autowired
    private IOrderSecondProcessLogService orderSecondProcessLogService;

    @Autowired
    private ICuttingBedService cuttingBedService;

    @Autowired
    private ICuttingBedSizeService cuttingBedSizeService;

    @Autowired
    private IOrderSewingService orderSewingService;

    /**
     * 创建订单二次工艺
     *
     * @param sysOrderPart
     * @param goodsPartId
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public void createOrderSecondProcess(SysOrderPart sysOrderPart, Long goodsPartId, SysOrder sysOrder) {

        List<SysGoodsSecondProcess> goodsSecondProcesses = this.goodsSecondProcessService.list(new LambdaQueryWrapper<SysGoodsSecondProcess>()
                .eq(SysGoodsSecondProcess::getGoodsPartId, goodsPartId)
        );

        if (CollUtil.isEmpty(goodsSecondProcesses)) {
            return;
        }

        int isFirst = Convert.toInt(sysOrder.getIsFirst(), 0);
        Map<String, String> cuttingSecondProcessMap = new HashMap<>();
        if (isFirst == 0) {
            // 非首单，获取首单中的裁片二次工艺信息映射
            cuttingSecondProcessMap.putAll(firstOrderCuttingSecondProcessMap(sysOrder.getSku()));
        }

        List<SysOrderSecondProcess> orderSecondProcesses = goodsSecondProcesses.stream().map(goodsSecondProcess -> {
            SysOrderSecondProcess sysOrderSecondProcess = new SysOrderSecondProcess();
            BeanUtils.copyProperties(goodsSecondProcess, sysOrderSecondProcess);
            sysOrderSecondProcess.setOrderId(sysOrderPart.getOrderId());
            sysOrderSecondProcess.setOrderPartId(sysOrderPart.getId());
            sysOrderSecondProcess.setProduceOrderId(sysOrderPart.getProduceOrderId());

            // 判断是否是面辅料二次工艺
            if (Objects.equals(goodsSecondProcess.getType(), GoodsEnum.GoodsSecondProcessTypeEnum.DOUGH_AND_AUXILIARY_MATERIALS.getType())) {
                sysOrderSecondProcess.setSendMaterialsQuantity(null);
                sysOrderSecondProcess.setRecycledMaterialsQuantity(null);
                sysOrderSecondProcess.setActualIssuanceCraftFactory(null);
                sysOrderSecondProcess.setActualIssuancePrice(null);

                // 判断是否是首单
                if (isFirst == 1) {
                    sysOrderSecondProcess.setCuttingSecondProcess(null);
                    sysOrderSecondProcess.setStatus(OrderSecondProcessEnum.StatusEnum.TO_BE_CONFIRMED.getStatus());
                } else {
                    // 非首单，需要判断首单中该工艺的裁片二次工艺状态
//                    String key = String.format("%s_%s", sysOrder.getProduceOrderId(), goodsSecondProcess.getProcessName());
                    if (cuttingSecondProcessMap.containsKey(goodsSecondProcess.getProcessName())) {
                        String cuttingSecondProcess = cuttingSecondProcessMap.get(goodsSecondProcess.getProcessName());
                        sysOrderSecondProcess.setCuttingSecondProcess(cuttingSecondProcess);
                        if (StrUtil.isEmpty(cuttingSecondProcess)) {
                            sysOrderSecondProcess.setStatus(OrderSecondProcessEnum.StatusEnum.TO_BE_CONFIRMED.getStatus());
                        } else {
                            sysOrderSecondProcess.setStatus(OrderSecondProcessEnum.StatusEnum.TO_GO.getStatus());
                        }
                    } else {
                        // 虽然是非首单，但是存在从shein接口过来的直接就是返单数据，此时需要设置状态为待发起
                        sysOrderSecondProcess.setCuttingSecondProcess(null);
                        sysOrderSecondProcess.setStatus(OrderSecondProcessEnum.StatusEnum.TO_BE_CONFIRMED.getStatus());
                    }

                }
            }

            sysOrderSecondProcess.setCreateBy(SecurityUtils.getUserId());
            sysOrderSecondProcess.setUpdateBy(SecurityUtils.getUserId());
            return sysOrderSecondProcess;
        }).collect(Collectors.toList());
        super.saveBatch(orderSecondProcesses);

    }

    /**
     * 查询商品报价单部件二次工艺信息
     *
     * @param orderPartId
     * @return
     */
    @Override
    public List<GoodsQuotationSheetPartSecondProcessDTO> queryQuotationSheetPartSecondProcesses(Long orderPartId) {
        return super.list(new LambdaQueryWrapper<SysOrderSecondProcess>()
                .eq(SysOrderSecondProcess::getOrderPartId, orderPartId)
                .eq(SysOrderSecondProcess::getType, GoodsEnum.GoodsSecondProcessTypeEnum.DOUGH_AND_AUXILIARY_MATERIALS.getType())
        ).stream().map(this::convertSysOrderProcess2GoodsQuotationSheetPartSecondProcessDTO).collect(Collectors.toList());
    }

    /**
     * 查询商品报价单部件成衣二次工艺信息
     *
     * @param orderPartId
     * @return
     */
    @Override
    public List<GoodsQuotationSheetPartSecondProcessDTO> queryQuotationSheetPartReadyToWearSecondProcesses(Long orderPartId) {
        return super.list(new LambdaQueryWrapper<SysOrderSecondProcess>()
                .eq(SysOrderSecondProcess::getOrderPartId, orderPartId)
                .eq(SysOrderSecondProcess::getType, GoodsEnum.GoodsSecondProcessTypeEnum.READY_TO_WEAR.getType())
        ).stream().map(this::convertSysOrderProcess2GoodsQuotationSheetPartSecondProcessDTO).collect(Collectors.toList());
    }

    /**
     * 分页查询
     *
     * @param queryDto
     * @return
     */
    @Override
    public Page<QueryOrderSecondProcessPageQueryVO> page(QueryOrderSecondProcessPageQueryDTO queryDto) {
        DataPermissionThreadLocal.setDataPermission(DataScope.ORDER_FACTORY,"o.factory_permission");
        Page<QueryOrderSecondProcessPageQueryVO> page = Condition.page(queryDto);
        return super.getBaseMapper().page(page, queryDto);
    }

    /**
     * 二次工艺确认
     *
     * @param requestDto
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public void confirmSecondProcess(ConfirmSecondProcessDTO requestDto) {
        SysOrderSecondProcess orderSecondProcessById = this.getOrderSecondProcessById(requestDto.getId());
        orderSecondProcessById.setCuttingSecondProcess(requestDto.getCuttingSecondProcess());
        orderSecondProcessById.setStatus(OrderSecondProcessEnum.StatusEnum.TO_GO.getStatus());
        super.updateById(orderSecondProcessById);

        // 添加操作日志
        this.orderSecondProcessLogService.addLog(orderSecondProcessById.getId(), "工艺确认", SecurityUtils.getUsername());
    }

    /**
     * 修改裁片二次工艺
     *
     * @param requestDto
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public void updateCuttingSecondProcess(UpdateCuttingSecondProcessDTO requestDto) {
        SysOrderSecondProcess orderSecondProcessById = this.getOrderSecondProcessById(requestDto.getId());
        orderSecondProcessById.setCuttingSecondProcess(requestDto.getCuttingSecondProcess());
        super.updateById(orderSecondProcessById);

        // 添加操作日志
        this.orderSecondProcessLogService.addLog(orderSecondProcessById.getId(), "修改裁片二次工艺", SecurityUtils.getUsername());
    }

    /**
     * 工艺发起
     *
     * @param requestDto
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public void processInitiation(ProcessInitiationDTO requestDto) {
        SysOrderSecondProcess orderSecondProcessById = this.getOrderSecondProcessById(requestDto.getId());
        orderSecondProcessById.setActualIssuanceCraftFactory(requestDto.getActualIssuanceCraftFactory());
        orderSecondProcessById.setActualIssuancePrice(requestDto.getActualIssuancePrice());
        orderSecondProcessById.setSendMaterialsQuantity(requestDto.getSendMaterialsQuantity());
        orderSecondProcessById.setInitiationTime(new Date());
        orderSecondProcessById.setStatus(OrderSecondProcessEnum.StatusEnum.ALREADY_GOING.getStatus());
        super.updateById(orderSecondProcessById);

        // 添加操作日志
        this.orderSecondProcessLogService.addLog(orderSecondProcessById.getId(), "工艺发起", SecurityUtils.getUsername());
    }

    /**
     * 工艺回料
     *
     * @param requestDto
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public void processRecycledMaterial(ProcessRecycledMaterialDTO requestDto) {
        SysOrderSecondProcess orderSecondProcessById = this.getOrderSecondProcessById(requestDto.getId());
        if (!Objects.equals(requestDto.getRecycledMaterialsQuantity(), orderSecondProcessById.getSendMaterialsQuantity())) {
            Assert.notBlank(requestDto.getMissingQuantityReason(),"少数原因未填写");
        }

        orderSecondProcessById.setRecycledMaterialsQuantity(requestDto.getRecycledMaterialsQuantity());
        orderSecondProcessById.setReturnMaterialTime(new Date());
        orderSecondProcessById.setReturnMaterialRemark(requestDto.getReturnMaterialRemark());
        orderSecondProcessById.setMissingQuantityReason(requestDto.getMissingQuantityReason());
        orderSecondProcessById.setStatus(OrderSecondProcessEnum.StatusEnum.ALREADY_RETURN_MATERIAL.getStatus());
        super.updateById(orderSecondProcessById);

        // 添加操作日志
        this.orderSecondProcessLogService.addLog(orderSecondProcessById.getId(), "工艺回料", SecurityUtils.getUsername());

        // 生成车缝数据(因为之前生成车缝数据逻辑中如果二次工艺未回料，导致没有生成车缝数据，回料后需要再次判定是否生成车缝数据)
        this.orderSewingService.createOrderSewing(Convert.toLong(orderSecondProcessById.getProduceOrderId()));
    }

    /**
     * 获取默认的发料数量
     *
     * @param produceOrderId
     * @return
     */
    @Override
    public Integer getDefaultRecycledMaterialsQuantity(Long produceOrderId) {
        SysCuttingBed sysCuttingBed = this.cuttingBedService.getOne(new LambdaQueryWrapper<SysCuttingBed>()
                .eq(SysCuttingBed::getProduceOrderId, produceOrderId)
                .orderByDesc(SysCuttingBed::getCreateTime)
                .last("limit 1")
        );
        Assert.notNull(sysCuttingBed, "该订单对应的裁床数据不存在");

        // 获取该裁床对应的所有尺码数量
        Map<Long, Integer> partSizeQuantityMap = this.cuttingBedSizeService.list(new LambdaQueryWrapper<SysCuttingBedSize>()
                .eq(SysCuttingBedSize::getCuttingBedId, sysCuttingBed.getId())
                .eq(SysCuttingBedSize::getStatus, CommonStatus.ENABLE.code)
        ).stream().collect(Collectors.groupingBy(SysCuttingBedSize::getOrderPartId,
                Collectors.summingInt(size -> size.getQuantity() == null ? 0 : size.getQuantity())));
        return partSizeQuantityMap.values().stream()
                .min(Integer::compareTo)
                .orElse(0);
    }

    /**
     * 统计指定查询条件下的各个状态的数量
     *
     * @param queryDto
     * @return
     */
    @Override
    public Map<Integer, Integer> getStatusTotals(QueryOrderSecondProcessPageQueryDTO queryDto) {
        // 去除状态过滤
        queryDto.setSecondProcessStatus(null);
        // 添加数据权限过滤
        String factoryPermissionsJsonStr = AuthUtil.getLoginUserFactoryPermissionsJsonStr();
        List<SecondProcessStatusQuantityTotal> statusTotals = super.getBaseMapper().getStatusTotals(queryDto,factoryPermissionsJsonStr);
        Map<Integer, Integer> existsStatusQuantityMap = statusTotals.stream().collect(Collectors.toMap(SecondProcessStatusQuantityTotal::getSecondProcessStatus, SecondProcessStatusQuantityTotal::getQuantity));

        // 编辑所有状态
        Map<Integer, Integer> statusTotalsMap = new HashMap<>();
        for (OrderSecondProcessEnum.StatusEnum statusEnum : OrderSecondProcessEnum.StatusEnum.values()) {
            if (existsStatusQuantityMap.containsKey(statusEnum.getStatus())) {
                statusTotalsMap.put(statusEnum.getStatus(), MapUtil.getInt(existsStatusQuantityMap, statusEnum.getStatus(), 0));
            } else {
                statusTotalsMap.put(statusEnum.getStatus(), 0);
            }
        }

        return statusTotalsMap;
    }

    /**
     * 判断指定订单是否存在二次工艺
     *
     * @param produceOrderId
     * @return
     */
    @Override
    public boolean isExistOrderSecondProcess(Long produceOrderId) {
        int count = super.count(new LambdaQueryWrapper<SysOrderSecondProcess>()
                .eq(SysOrderSecondProcess::getProduceOrderId, produceOrderId)
                .eq(SysOrderSecondProcess::getType, GoodsEnum.GoodsSecondProcessTypeEnum.DOUGH_AND_AUXILIARY_MATERIALS.getType())
        );
        return count > 0;
    }

    /**
     * 判断指定订单是否存在裁片二次工艺是否需要确认
     *
     * @param produceOrderId
     * @return
     */
    @Override
    public boolean isExistCuttingSecondProcessNeedConfirm(Long produceOrderId) {
        int count = super.count(new LambdaQueryWrapper<SysOrderSecondProcess>()
                .eq(SysOrderSecondProcess::getProduceOrderId, produceOrderId)
                .eq(SysOrderSecondProcess::getType, GoodsEnum.GoodsSecondProcessTypeEnum.DOUGH_AND_AUXILIARY_MATERIALS.getType())
                .eq(SysOrderSecondProcess::getStatus, OrderSecondProcessEnum.StatusEnum.TO_BE_CONFIRMED.getStatus())
        );
        return count > 0;
    }

    /**
     * 判断二次工艺中裁片二次工艺为是的是否都已回料了
     *
     * @param produceOrderId
     * @return
     */
    @Override
    public boolean isAllCuttingSecondProcessReturned(Long produceOrderId) {
        List<SysOrderSecondProcess> orderSecondProcesses = super.list(new LambdaQueryWrapper<SysOrderSecondProcess>()
                .eq(SysOrderSecondProcess::getProduceOrderId, produceOrderId)
                .eq(SysOrderSecondProcess::getType, GoodsEnum.GoodsSecondProcessTypeEnum.DOUGH_AND_AUXILIARY_MATERIALS.getType())
                .eq(SysOrderSecondProcess::getCuttingSecondProcess, OrderSecondProcessEnum.CuttingSecondProcessEnum.IS.getValue())
        );
        if (CollUtil.isEmpty(orderSecondProcesses)) {
            // 没有裁片二次工艺为是的数据，则判定为都通过了
            return true;
        }
        return orderSecondProcesses.stream().allMatch(x -> Objects.equals(x.getStatus(), OrderSecondProcessEnum.StatusEnum.ALREADY_RETURN_MATERIAL.getStatus()));
    }

    /**
     * 根据二次工艺id获取对应的数据库对象
     *
     * @param id
     * @return
     */
    private SysOrderSecondProcess getOrderSecondProcessById(Long id) {
        SysOrderSecondProcess secondProcess = super.getById(id);
        Assert.notNull(secondProcess, "该二次工艺数据不存在");
        return secondProcess;
    }

    /**
     * 获取该sku对应的首单中裁片二次工艺数据映射
     *
     * @param sku
     * @return
     */
    private Map<String, String> firstOrderCuttingSecondProcessMap(String sku) {
        SysOrder sysOrder = this.orderService.getOne(new LambdaQueryWrapper<SysOrder>()
                .eq(SysOrder::getSku, sku)
                .eq(SysOrder::getIsFirst, 1)
        );
        if (sysOrder == null) {
            // 该sku没有首单订单
            return new HashMap<>();
        }

        return super.list(new LambdaQueryWrapper<SysOrderSecondProcess>()
                        .eq(SysOrderSecondProcess::getOrderId, sysOrder.getId())
                        .eq(SysOrderSecondProcess::getType, GoodsEnum.GoodsSecondProcessTypeEnum.DOUGH_AND_AUXILIARY_MATERIALS.getType())
//        ).stream().collect(Collectors.toMap(x -> String.format("%s_%s", x.getProduceOrderId(), x.getProcessName()),
        ).stream().collect(Collectors.toMap(SysOrderSecondProcess::getProcessName,
                x -> StrUtil.nullToEmpty(x.getCuttingSecondProcess()), (a, b) -> b));
    }


    /**
     * SysOrderSecondProcess 转换成 GoodsQuotationSheetPartSecondProcessDTO 对象
     *
     * @param sysOrderSecondProcess
     * @return
     */
    private GoodsQuotationSheetPartSecondProcessDTO convertSysOrderProcess2GoodsQuotationSheetPartSecondProcessDTO(SysOrderSecondProcess sysOrderSecondProcess) {
        GoodsQuotationSheetPartSecondProcessDTO partSecondProcessDTO = new GoodsQuotationSheetPartSecondProcessDTO();
        BeanUtils.copyProperties(sysOrderSecondProcess, partSecondProcessDTO);

        BigDecimal amount = NumberUtil.mul(
                NumberUtil.round(StrUtil.blankToDefault(sysOrderSecondProcess.getUnitPrice(), "0"), 2),
                NumberUtil.round(StrUtil.blankToDefault(sysOrderSecondProcess.getSimpleUse(), "0"), 2)
        );
        amount = NumberUtil.round(amount, 2);
        partSecondProcessDTO.setAmount(amount.toPlainString());
        return partSecondProcessDTO;
    }


}
